package com.zbkj.common.utils;

import java.util.ArrayList;
import java.util.List;

/**
 * 经纬度工具类
 */
public class GeoUtils {


    /**
     * 判断一个经纬度点是否在多个经纬度点组成的多边形（圈）内
     *
     * @param lat     纬度
     * @param lon     经度
     * @param polygon
     * @return
     */
//    public static boolean isPointInPolygon(double lat, double lon, List<Point> polygon) {
//        boolean inside = false;
//        int n = polygon.size();
//
//        // 遍历多边形的每条边
//        for (int i = 0, j = n - 1; i < n; j = i++) {
//            double latI = polygon.get(i).getLat();
//            double lonI = polygon.get(i).getLon();
//            double latJ = polygon.get(j).getLat();
//            double lonJ = polygon.get(j).getLon();
//
//            // 检查点是否与顶点重合
//            if (lat == latI && lon == lonI) return true;
//
//            // 判断边是否与射线相交
//            boolean intersect = ((lonI > lon) != (lonJ > lon))
//                    && (lat < (latJ - latI) * (lon - lonI) / (lonJ - lonI) + latI);
//
//            if (intersect) inside = !inside;
//        }
//        return inside;
//    }
//
//    // 辅助类：存储经纬度点
//    public static class Point {
//        private double lat; // 纬度
//        private double lon; // 经度
//
//        public Point(double lat, double lon) {
//            this.lat = lat;
//            this.lon = lon;
//        }
//
//        public double getLat() {
//            return lat;
//        }
//
//        public double getLon() {
//            return lon;
//        }
//    }
//
//    // 示例用法
//    public static void main(String[] args) {
//        // 定义多边形顶点（按顺序）
//        List<Point> polygon = new ArrayList<>();
//        polygon.add(new Point( 39.93842,116.37956)); // 点1
//        polygon.add(new Point( 39.91308,116.38078)); // 点2
//        polygon.add(new Point( 39.93964,116.42390)); // 点3
//        polygon.add(new Point(39.91419,116.42469)); // 点4
//        polygon.add(new Point( 39.93842,116.37956)); // 点1
////        polygon.add(new Point(40.0, -75.0)); // 闭合：与点1相同
//
//        // 待测点
//        double testLat = 39.93212;
//        double testLon =  116.40744;
//
//        // 检查是否在多边形内
//        boolean inside = isPointInPolygon(testLat, testLon, polygon);
//        System.out.println("点是否在多边形内: " + inside); // 输出结果
//    }


//    // 使用小量避免浮点精度问题
//    private static final double EPSILON = 1e-10;
//
//    public static boolean isPointInPolygon(double lat, double lon, List<Point> polygon) {
//        if (polygon.size() < 3) return false;
//
//        boolean inside = false;
//        int n = polygon.size();
//
//        // 自动闭合多边形（如果未闭合）
//        Point first = polygon.get(0);
//        Point last = polygon.get(n - 1);
//        if (Math.abs(first.lat - last.lat) > EPSILON ||
//                Math.abs(first.lon - last.lon) > EPSILON) {
//            polygon = new ArrayList<>(polygon);
//            polygon.add(first);
//            n = polygon.size();
//        }
//
//        // 遍历所有边
//        for (int i = 0, j = n - 1; i < n; j = i++) {
//            Point p1 = polygon.get(i);
//            Point p2 = polygon.get(j);
//
//            // 检查点是否在顶点上
//            if (isSamePoint(lat, lon, p1.lat, p1.lon)) {
//                return true;
//            }
//
//            // 检查点是否在边上
//            if (isOnEdge(lat, lon, p1.lat, p1.lon, p2.lat, p2.lon)) {
//                return true;
//            }
//
//            // 改进的射线法判断
//            if ((p1.lon > lon) != (p2.lon > lon)) {
//                // 计算精确交点
//                double intersectLat = calculateIntersection(lat, lon, p1, p2);
//
//                // 避免浮点误差
//                if (Math.abs(intersectLat - lat) < EPSILON) {
//                    return true; // 点在边上
//                }
//
//                if (intersectLat > lat) {
//                    inside = !inside;
//                }
//            }
//        }
//        return inside;
//    }
//
//    // 精确计算交点纬度
//    private static double calculateIntersection(double lat, double lon, Point p1, Point p2) {
//        // 处理垂直线
//        if (Math.abs(p2.lon - p1.lon) < EPSILON) {
//            return p1.lat;
//        }
//
//        // 标准直线方程
//        double slope = (p2.lat - p1.lat) / (p2.lon - p1.lon);
//        return p1.lat + (lon - p1.lon) * slope;
//    }
//
//    // 判断两点是否相同（考虑浮点误差）
//    private static boolean isSamePoint(double lat1, double lon1, double lat2, double lon2) {
//        return Math.abs(lat1 - lat2) < EPSILON &&
//                Math.abs(lon1 - lon2) < EPSILON;
//    }
//
//    // 判断点是否在边上
//    private static boolean isOnEdge(double lat, double lon,
//                                    double lat1, double lon1,
//                                    double lat2, double lon2) {
//        // 三点共线检测
//        double cross = (lon - lon1) * (lat2 - lat1) - (lat - lat1) * (lon2 - lon1);
//        if (Math.abs(cross) > EPSILON) return false;
//
//        // 检查点是否在线段范围内
//        double dot = (lon - lon1) * (lon2 - lon1) + (lat - lat1) * (lat2 - lat1);
//        if (dot < 0) return false;
//
//        double lenSq = (lon2 - lon1) * (lon2 - lon1) + (lat2 - lat1) * (lat2 - lat1);
//        return dot <= lenSq;
//    }
//
//    public static class Point {
//        public final double lat;
//        public final double lon;
//
//        public Point(double lat, double lon) {
//            this.lat = lat;
//            this.lon = lon;
//        }
//    }
//
//    // 测试方法
//    public static void main(String[] args) {
//        // 创建正方形多边形
//        List<Point> polygon = new ArrayList<>();
//        polygon.add(new Point(0, 0));
//        polygon.add(new Point(0, 10));
//        polygon.add(new Point(10, 10));
//        polygon.add(new Point(10, 0));
//
//
//        // 测试点
//        System.out.println("内部点: " + isPointInPolygon(5, 5, polygon)); // true
//        System.out.println("边界点: " + isPointInPolygon(0, 5, polygon)); // true
//        System.out.println("顶点: " + isPointInPolygon(0, 0, polygon));  // true
//        System.out.println("外部点: " + isPointInPolygon(-5, 5, polygon)); // false
//        System.out.println("凹点: " + isPointInPolygon(5, -5, polygon)); // false
//
//        // 测试凹多边形
//        List<Point> concavePoly = new ArrayList<>();
////        concavePoly.add(new Point(0, 0));
////        concavePoly.add(new Point(5, 10));
////        concavePoly.add(new Point(10, 0));
////        concavePoly.add(new Point(7, 5));
////        concavePoly.add(new Point(3, 5));
//        concavePoly.add(new Point( 30.49141,114.46348)); // 点1
//        concavePoly.add(new Point( 30.49360,114.48713)); // 点2
//        concavePoly.add(new Point( 30.47751,114.48773)); // 点3
//        concavePoly.add(new Point(30.47542,114.46016)); // 点4
////        double testLat = 39.93212;
////        double testLon =  116.40744;
//        //        private double lat; // 纬度
////        private double lon; // 经度
//        System.out.println("凹形内1111: " + isPointInPolygon(30.49313,  114.47694, concavePoly)); // true
//        System.out.println("凹形外2222: " + isPointInPolygon(30.49360, 114.48713, concavePoly)); // false
//        System.out.println("凹形外2222: " + isPointInPolygon(39.95067, 116.40576, concavePoly)); // false
//    }


    /**
     * 判断一个点是否在多边形内部；在多边形边上也不算是在多边形内部
     *
     * @param polygon 经纬度点组成的多边形
     * @param point   经纬度点
     * @return 点是否在多边形内
     */
    public static boolean isInside(List<Double[]> polygon, Double[] point) {
        boolean inside = false;
        for (int i = 0, j = polygon.size() - 1; i < polygon.size(); j = i++) {
            if (((polygon.get(i)[1] > point[1]) != (polygon.get(j)[1] > point[1])) &&
                    (point[0] < (polygon.get(j)[0] - polygon.get(i)[0]) * (point[1] - polygon.get(i)[1]) /
                            (polygon.get(j)[1] - polygon.get(i)[1]) + polygon.get(i)[0])) {
                inside = !inside;
            }
        }
        return inside;
    }

    public static void main(String[] args) {
        // 定义点
        Double[] point = new Double[]{114.50203, 30.48759};

        // 定义多边形，沿顺时针或逆时针方向，依次添加点的经纬度
        List<Double[]> polygon = new ArrayList<>();

        polygon.add(new Double[]{114.46348, 30.49141});
        polygon.add(new Double[]{114.48713, 30.49360});
        polygon.add(new Double[]{114.48773, 30.47751});
        polygon.add(new Double[]{114.46016, 30.47542});
        System.out.println(isInside(polygon, point));
    }


}
